import is.glif.fenius.ws.connection.v1.service.ConnectionService;
import is.glif.fenius.ws.connection.v1.service.Connection;
import is.glif.fenius.ws.connection.v1.service.ExternalFaultException;
import is.glif.fenius.ws.connection.v1.service.InternalFaultException;

import is.glif.fenius.ws.connection.v1._interface.IsReservableReq;
import is.glif.fenius.ws.connection.v1._interface.IsReservableResp;
import is.glif.fenius.ws.connection.v1._interface.ListReq;
import is.glif.fenius.ws.connection.v1._interface.ListResp;
import is.glif.fenius.ws.connection.v1._interface.QueryReq;
import is.glif.fenius.ws.connection.v1._interface.QueryResp;
import is.glif.fenius.ws.connection.v1._interface.ReleaseReq;
import is.glif.fenius.ws.connection.v1._interface.ReleaseResp;
import is.glif.fenius.ws.connection.v1._interface.ReserveReq;
import is.glif.fenius.ws.connection.v1._interface.ReserveResp;

import is.glif.fenius.ws.connection.v1.faults.ExternalFault;
import is.glif.fenius.ws.connection.v1.faults.InternalFault;
// import is.glif.fenius.ws.connection.v1.faults.ObjectFactory;

import is.glif.fenius.ws.connection.v1.types.ActivationStatus;
import is.glif.fenius.ws.connection.v1.types.Advance;
import is.glif.fenius.ws.connection.v1.types.Avp;
import is.glif.fenius.ws.connection.v1.types.AvpSeq;
import is.glif.fenius.ws.connection.v1.types.Credentials;
import is.glif.fenius.ws.connection.v1.types.Edge;
import is.glif.fenius.ws.connection.v1.types.Immediate;
import is.glif.fenius.ws.connection.v1.types.ListFilter;
import is.glif.fenius.ws.connection.v1.types.ObjectFactory;
import is.glif.fenius.ws.connection.v1.types.Reservable;
import is.glif.fenius.ws.connection.v1.types.Reservation;
import is.glif.fenius.ws.connection.v1.types.ReservationStatus;
import is.glif.fenius.ws.connection.v1.types.Schedule;
import is.glif.fenius.ws.connection.v1.types.Topology;
import is.glif.fenius.ws.connection.v1.types.Vertex;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;

import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.configuration.security.FiltersType;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;

import java.util.List;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.GregorianCalendar;

import java.net.URL;


public class ConnectionTest {
    static boolean debug = false;
    static boolean is_measure = false;

    public static String srcEndpoint = "aist.glambda.net:AKIBA:X";
    public static String dstEndpoint = "aist.glambda.net:AKIBA:PS";
    // public static String vlanid = "1799";
    // public static String vlanid = "3103";
    public static String vlanid = "1788";
    public static int bw = 100;

    static boolean isUseSSL = true;
    static boolean isUseBasicHttpAuth = true;
    // static boolean isUseSSL = false;
    // static boolean isUseBasicHttpAuth = false;

    static String timezone = "UTC";
    static QName qname = new QName("http://fenius.glif.is/ws/connection/v1/service", "connection");
    // String surl = "http://jupiter.es.net:8080/DraftFeniusService?wsdl";
    // String surl = "http://150.183.236.50:8081/DraftFeniusService?wsdl";
    // String surl = "http://amy.phosphorus.i2cat.cat:8080/DraftFeniusService?wsdl";
    // static String purl = "http://163.220.30.149:8080/ConnectionService?wsdl";
    // static String surl = "https://163.220.30.149:8080/ConnectionService?wsdl";
    static String purl = "http://192.50.75.193:8080/ConnectionService?wsdl";
    static String surl = "https://192.50.75.193:8080/ConnectionService?wsdl";

    // static String sslTruststorePath = "config/client-truststore";
    // static String sslTruststorePass = "clientkey";
    // static String sslKeystorePath = "config/client-keystore";
    // static String sslKeystorePass = "clientkey";
    static String sslTruststorePath = "../config/myserver-t";
    static String sslTruststorePass = "serverkey";
    static String sslKeystorePath = "../config/myclient";
    static String sslKeystorePass = "clientkey";
    static String httpUserName = "username";
    static String httpUserPass = "password";

    public ConnectionService service = null;
    private List<Credentials> lcredentials = null;
    static public String keepId = null;

    public ConnectionTest() 
    {
	if (isUseSSL) {
	    service = initSoap(surl, qname);
	} else {
	    service = initSoap(purl, qname);
	}
    }

    static private ConnectionService initSoap(String wsdlLocation, QName serviceName)
    {
        if (isUseSSL) {
            System.setProperty("javax.net.ssl.trustStoreType", "jks");
            System.setProperty("javax.net.ssl.trustStore", sslTruststorePath);
            System.setProperty("javax.net.ssl.trustStorePassword", sslTruststorePass);
              
            System.setProperty("javax.net.ssl.keyStoreType", "jks");
            System.setProperty("javax.net.ssl.keyStore", sslKeystorePath);
            System.setProperty("javax.net.ssl.keyStorePassword", sslKeystorePass);
        }
        
        URL wsdlURL = null;

        try {
            wsdlURL = new URL(wsdlLocation);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
        
        Connection ss = new Connection(wsdlURL, serviceName);
        ConnectionService port = ss.getConnectionServicePort();
        
        org.apache.cxf.endpoint.Client client = ClientProxy.getClient(port); 
        HTTPConduit httpConduit = (HTTPConduit) client.getConduit();
        HTTPClientPolicy cp = new HTTPClientPolicy();
        Integer timeout = new Integer(10*1000);
        Long connTO = new Long(timeout);
        Long rcvTO = new Long(timeout);
        
        cp.setConnectionTimeout(connTO);
        cp.setReceiveTimeout(rcvTO);
        httpConduit.setClient(cp);

        if (isUseSSL) {
	    /*
	    org.apache.cxf.endpoint.Client client2 = ClientProxy.getClient(port);
	    HTTPConduit httpConduit2 = (HTTPConduit) client2.getConduit();
 	    TLSClientParameters tlsParams = new TLSClientParameters(); 
	    tlsParams.setDisableCNCheck(true); 

	    FiltersType filter = new FiltersType(); 
	    filter.getInclude().add(".*_EXPORT_.*"); 
	    filter.getInclude().add(".*_EXPORT1024_.*"); 
	    filter.getInclude().add(".*_WITH_DES_.*"); 
	    filter.getInclude().add(".*_WITH_NULL_.*"); 
	    filter.getExclude().add(".*_DH_anon_.*"); 
	    tlsParams.setCipherSuitesFilter(filter); 
        
	    httpConduit2.setTlsClientParameters(tlsParams); 
	    */
 	    TLSClientParameters tlsParams = new TLSClientParameters(); 
	    tlsParams.setDisableCNCheck(true); 
	    // tlsParams.setDisableCNCheck(false); 

	    FiltersType filter = new FiltersType(); 
	    filter.getInclude().add(".*_EXPORT_.*"); 
	    filter.getInclude().add(".*_EXPORT1024_.*"); 
	    filter.getInclude().add(".*_WITH_DES_.*"); 
	    filter.getInclude().add(".*_WITH_NULL_.*"); 
	    filter.getExclude().add(".*_DH_anon_.*"); 
	    tlsParams.setCipherSuitesFilter(filter); 
        
	    httpConduit.setTlsClientParameters(tlsParams); 
        }

        if (isUseBasicHttpAuth) {
            BindingProvider bp = (BindingProvider) port;
            bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, 
				       httpUserName);
            bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, 
				       httpUserPass);
        }

	return port;
    }

    private void listcopy(List<Credentials> src, List<Credentials> dst)
    {
	if (src == null) return;
	if (dst == null) return;
	
	int size = src.size();
	for (int i = 0; i < size; i ++) {
	    Credentials c = src.get(i);
	    if (c != null) dst.add(c);
	}
    }

    private Reservation getReservation(Reservable param)
    {
	Reservation resv = new Reservation();

	resv.setTopology(param.getTopology());
	resv.setSchedule(param.getSchedule());
	resv.setAdhocParams(param.getAdhocParams());

	{
	    List<String> dlist = resv.getTag();
	    List<String> slist = param.getTag();
	    int size = slist.size();
	    for (int i = 0; i < size; i++) {
		dlist.add(slist.get(i));
	    }
	}

	{
	    List<String> dlist = resv.getDescription();
	    List<String> slist = param.getDescription();
	    int size = slist.size();
	    for (int i = 0; i < size; i++) {
		dlist.add(slist.get(i));
	    }
	}
	return resv;
    }
	
    public Reservation reserve(Reservable param, List<Credentials> credentials) 
    {
	Reservation err_resv = getReservation(param);
	err_resv.setReservationStatus(ReservationStatus.FAILED);

	if (service == null) return err_resv;

	try {
	    if (debug) printParams("try reserve, param:", param);
	    ReserveReq req = new ReserveReq();
	    req.setReservationParams(param);
	    List<Credentials> cred = req.getCredentials();
	    listcopy(credentials, cred);

	    long T_create_s = System.nanoTime();
	    if (false) check(param, credentials);
	    ReserveResp res = service.reserve(req);
	    long T_create_e = System.nanoTime();

	    if (is_measure) System.err.println("\t reserve():" + 
					      (T_create_e - T_create_s)/1000);

	    Reservation resv = res.getReservation();
	    if (true) printParams("reserved param:", (Reservable)resv);

	    keepId = resv.getReservationId();

	    if (false) {
		query(resv.getReservationId(), credentials);
		release(resv.getReservationId(), credentials);
	    }

	    return resv;
	} catch (Exception e) {
	    e.printStackTrace();
	    return err_resv;
	}
    }

    public boolean release(String id, List<Credentials> credentials)
    {
	if (service == null) return false;

	try {
	    if (debug) System.err.println("try release, id=" +  id);

	    ReleaseReq req = new ReleaseReq();
	    req.setReservationId(id);
	    List<Credentials> cred = req.getCredentials();
	    listcopy(credentials, cred);

	    long T_create_s = System.nanoTime();
	    ReleaseResp res = service.release(req);
	    long T_create_e = System.nanoTime();

	    if (is_measure) System.err.println("\t release():" + 
					      (T_create_e - T_create_s)/1000);

	    if (true) System.err.println("result released id=" +  id);
	} catch (Exception e) {
	    e.printStackTrace();
	    return false;
	}
	return true;
    }

    public boolean list(ListFilter filter, List<Credentials> credentials) 
    {
	if (service == null) return false;

	try {
	    if (debug) printFilter("try list, filter:", filter);

	    ListReq req = new ListReq();
	    req.setListFilter(filter);
	    List<Credentials> cred = req.getCredentials();
	    listcopy(credentials, cred);
	    
	    long T_create_s = System.nanoTime();
	    ListResp res = service.list(req);
	    long T_create_e = System.nanoTime();

	    if (is_measure) System.err.println("\t list():" + 
					      (T_create_e - T_create_s)/1000);

	    List<Reservation> resvs = res.getReservations();
	    if (resvs == null) return false;

	    int size = resvs.size();
	    for(int i = 0; i < size; i++) {
		Reservation resv = resvs.get(i);
		if (true) printParams("list param:", (Reservable) resv);
	    }
	} catch (Exception e) {
	    e.printStackTrace();
	    return false;
	}
	return true;
    }

    public boolean check(Reservable param, List<Credentials> credentials) 
    {
	if (service == null) return false;

	try {
	    if (debug) printParams("try check, param:", param);
	    IsReservableReq req = new IsReservableReq();
	    req.setReservationParams(param);
	    List<Credentials> cred = req.getCredentials();
	    listcopy(credentials, cred);

	    long T_create_s = System.nanoTime();
	    IsReservableResp res = service.isReservable(req);
	    long T_create_e = System.nanoTime();

	    if (is_measure) System.err.println("\t isReservable():" + 
					      (T_create_e - T_create_s)/1000);


	    boolean result = res.isResult();
	    if (true) System.err.println("check result:" + result);

	    return result;
	} catch (Exception e) {
	    e.printStackTrace();
	    return false;
	}
    }

    public Reservation query(String id, List<Credentials> credentials) 
    {
	Reservation err_resv = new Reservation();
	err_resv.setReservationId(id);
	err_resv.setReservationStatus(ReservationStatus.FAILED);

	if (service == null) return err_resv;

	try {
	    if (debug) System.err.println("try query, id:" + id);
	    QueryReq req = new QueryReq();
	    req.setReservationId(id);
	    List<Credentials> cred = req.getCredentials();
	    listcopy(credentials, cred);

	    long T_create_s = System.nanoTime();
	    QueryResp res = service.query(req);
	    long T_create_e = System.nanoTime();

	    if (is_measure) System.err.println("\t query():" + 
					      (T_create_e - T_create_s)/1000);

	    Reservation resv = res.getReservation();
	    if (true) printParams("query param:", (Reservable) resv);
	    return resv;
	} catch (Exception e) {
	    e.printStackTrace();
	    return err_resv;
	}
    }

    private String toStringList(List<String> list, String m)
    {
	String s = m + "";
	if (list == null) return s + "null\n";

	int size = list.size();
	for (int i = 0; i < size; i++) {
	    String o = list.get(i);
	    if (o == null) continue;
		
	    s += o + ":";
	}
	s += "\n";
	return s;
    }

    private void printFilter(String m, ListFilter p)
    {
	if (p == null) {
	    System.err.println(m + ": null\n");
	    return;
	}

	String s = m + ":\n";
	s += toStringList(p.getReservationId(), " id    : ");
	s += toStringList(p.getDescriptor(),    " desp  : ");

	{
	    XMLGregorianCalendar g = p.getStartsBefore();
	    if (g != null) 
		s += " start < " + XMLGregorianCalendarToString(g);
	}
	{
	    XMLGregorianCalendar g = p.getStartsAfter();
	    if (g != null) 
		s += " start > " + XMLGregorianCalendarToString(g);
	}
	{
	    XMLGregorianCalendar g = p.getEndsBefore();
	    if (g != null) 
		s += " end   < " + XMLGregorianCalendarToString(g);
	}
	{
	    XMLGregorianCalendar g = p.getEndsAfter();
	    if (g != null) 
		s += " end   > " + XMLGregorianCalendarToString(g);
	}

	System.err.println(s);
    }

    private void printParams(String m, Reservable p)
    {
	if (p == null) {
	    System.err.println(m + ": null\n");
	    return;
	}

	String s = m + ":\n";

	if (p instanceof Reservation) s += reservationToString((Reservation) p);
	    
	{
	    Topology t = p.getTopology();
	    if (t == null) t = new Topology();
	    s += " Topology\t: directed=" + t.isDirected() + "\n";

	    {
		List<Vertex> l = t.getVertices();
		int size = l.size();
		s += "         \t: vertex(endpoint) size=" + size + "\n";
		for (int i = 0; i < size; i++) {
		    Vertex o = l.get(i);
		    if (o == null) continue;

		    s += "         \t: vertex[" + i + "]: " + o.getResourceId() + 
			", " + o.getVertexId() + 
			", tech=" + avpToString(o.getTechParams()) +
			", adhoc=" + avpToString(o.getAdhocParams());
		    s += "\n";
		}
	    }
	    {
		List<Edge> l = t.getEdges();
		int size = l.size();
		s += "         \t: edges(path) size=" + size + "\n";
		for (int i = 0; i < size; i++) {
		    Edge o = l.get(i);
		    if (o == null) continue;

		    s += "         \t: edge[" + i + "]  : " + 
			o.getA() + "-" + o.getZ() + 
			", id=" + o.getEdgeId() + 
			", bw=" + o.getBandwidth() + "\n";
		}
	    }
	}
	{
	    Schedule sc = p.getSchedule();
	    if (sc == null) sc = new Schedule();

	    XMLGregorianCalendar g = sc.getCreated();
	    if (g != null) 
		s += " Schedule\t: created=" + XMLGregorianCalendarToString(g) + "\n";

	    Immediate im = sc.getImmediate();
	    if (im != null) 
		s += " Topology\t: immediate end=" + 
		    XMLGregorianCalendarToString(im.getEnd()) + 
		    ", duration=" + im.getDuration() + "\n";
	    
	    Advance ad = sc.getAdvance();
	    if (ad != null) 
		s += " Topology\t: advance start=" + 
		    XMLGregorianCalendarToString(ad.getStart()) + 
		    ", duration=" + ad.getDuration() + 
		    ", end=" + XMLGregorianCalendarToString(ad.getEnd()) + "\n";
	}

	s += toStringList(p.getTag(), " tag     \t: ");
	s += toStringList(p.getDescription(), " descript\t: ");

	{
	    AvpSeq avp = p.getAdhocParams();
	    s += " AvpSeg  \t: " + avpToString(avp) + "\n";
	}

	System.err.println(s);
    }

    private String avpToString(AvpSeq avp) 
    {
	if (avp == null) return "null";

	String s = "";
	List<Avp> lavp = avp.getAvp();

	int size = lavp.size();
	for (int i = 0; i < size; i++) {
	    Avp o = lavp.get(i);
	    if (o == null) continue;
	    s += o.getAttrName() + "=" + o.getAttrVal() + ", ";
	}
	return s;
    }

    private String reservationToString(Reservation r)
    {
	if (r == null) return "null";

	String s = "";
	s += " ReservationId\t: " + r.getReservationId() + "\n";
	s += " ReservationStatus\t: " + r.getReservationStatus() + "\n";
	s += " ActivationStatus\t: " + r.getActivationStatus() + "\n";
	return s;
    }

    private String XMLGregorianCalendarToString(XMLGregorianCalendar c)
    { 
	if (c == null) return "null";

	String s = c.getYear() + "/" + c.getMonth() + "/" + c.getDay();
	s += " " + c.getHour() + ":" + c.getMinute() + ":" + c.getSecond();
	return s;
    }
    
    private List<Credentials> getCredential()
    {
	if (lcredentials == null) {
	    lcredentials = new ArrayList<Credentials>();

	    Credentials credential = new Credentials();
	    credential.setUsername("fenius-user");
	    credential.setGroup("fenius-group");
	    lcredentials.add(credential);
	}

	return lcredentials;
    }


    public boolean createTest() 
    {
	int duration = 2; /* minute */
	int waitstart = 1; /* minute */

	Calendar startTime = Calendar.getInstance();
	startTime.setTime(new Date());
	startTime.set(Calendar.MILLISECOND, 0);
	startTime.add(Calendar.MINUTE, waitstart);
	Calendar endTime = (Calendar) startTime.clone();
	endTime.add(Calendar.MINUTE, duration);

	GregorianCalendar startgc = (GregorianCalendar) GregorianCalendar.getInstance();
	Date startDate = startTime.getTime();
	startgc.setTime(startDate);
	GregorianCalendar endgc = (GregorianCalendar) GregorianCalendar.getInstance();
	Date endDate = endTime.getTime();
	endgc.setTime(endDate);

	DatatypeFactory df = null;
	try {
	     df = DatatypeFactory.newInstance();
	} catch (javax.xml.datatype.DatatypeConfigurationException e) {
	    e.printStackTrace();
	    return false;
	}
	XMLGregorianCalendar startxgc = df.newXMLGregorianCalendar(startgc);
	XMLGregorianCalendar endxgc = df.newXMLGregorianCalendar(endgc);

	Reservable param = new Reservable();
	Topology topology = new Topology();
	topology.setDirected(false);
	param.setTopology(topology);

	{
	    Vertex apoint = new Vertex();
	    apoint.setResourceId(srcEndpoint);
	    apoint.setVertexId("0");
	    apoint.setDescription(srcEndpoint);
	    {
		AvpSeq atech = new AvpSeq();
		apoint.setTechParams(atech);

		Avp avp = new Avp();
		avp.setAttrName("eth.vlanTag");
		avp.setAttrVal("" + vlanid);

		List<Avp> lavp = atech.getAvp();
		lavp.add(avp);
	    }

	    Vertex zpoint = new Vertex();
	    zpoint.setResourceId(dstEndpoint);
	    zpoint.setVertexId("1");
	    zpoint.setDescription(dstEndpoint);
	    {
		AvpSeq ztech = new AvpSeq();
		zpoint.setTechParams(ztech);

		Avp avp = new Avp();
		avp.setAttrName("eth.vlanTag");
		avp.setAttrVal("" + vlanid);

		List<Avp> lavp = ztech.getAvp();
		lavp.add(avp);
	    }

	    List<Vertex> l = topology.getVertices();
	    l.add(apoint);
	    l.add(zpoint);
	}
	{
	    Edge e = new Edge();
	    e.setA(srcEndpoint);
	    e.setZ(dstEndpoint);
	    e.setEdgeId(srcEndpoint + "->" + dstEndpoint);
	    e.setDescription(srcEndpoint + "->" + dstEndpoint);
	    e.setBandwidth(bw);
	    e.setActivationStatus(ActivationStatus.INACTIVE);

	    List<Edge> l = topology.getEdges();
	    l.add(e);
	}

	Schedule schedule = new Schedule();
	param.setSchedule(schedule);
	schedule.setCreated(startxgc); /* required, but not used in fenius-soap */

	if (true) {
	    Advance advance =  new Advance();
	    schedule.setAdvance(advance);

	    advance.setStart(startxgc); /* required */
	    // advance.setEnd(endxgc);
	    advance.setDuration(duration * 60);
	} else {
	    Immediate immediate = new Immediate();
	    schedule.setImmediate(immediate);

	    immediate.setEnd(endxgc);
	    // advance.setDuration(duration * 60);
	}

	Reservation resv = reserve(param, getCredential());

	return true;
    }

    public boolean listTest() {
	ListFilter filter = new ListFilter();

	List<String> ids = filter.getReservationId();
	if (keepId != null) ids.add(keepId);
	
	return list(filter, getCredential());
    }
    
    public boolean cancelTest(String id) {
	return release(id, getCredential());
    }
    
    public static void main(String[] args) {
	// String timezone = "Asia/Tokyo";
	TimeZone.setDefault(TimeZone.getTimeZone(timezone));

	try {
	    ConnectionTest t = new ConnectionTest();

	    if (false) {
		int i = 10000;
		t.createTest();
		while (i > 0) {
		    // System.gc();
		    Thread.sleep(1000);
		    t.listTest();
		    i--;
		}
		System.exit(0);
	    }

	    if (args != null && args.length != 0 &&
		args[0].equals("create")) {
		t.createTest();
	    } else if (args != null && args.length != 0 &&
		       args[0].equals("list")) {
		t.keepId = null;
		if (args.length >= 2) t.keepId = args[1];
		t.listTest();
	    } else if (args != null && args.length != 0 &&
		       args[0].equals("cancel") && args[1] != null ) {
		t.cancelTest(args[1]);
	    } else {
		String s = "Test create | list | cancel id \n" +
		    "\t create:      create reservation\n" +
		    "\t list:        list reservation\n" +
		    "\t cancel id:   cancel reservation which has id \n";
		System.err.println(s);
	    }
	} catch (Exception ex) {
	    ex.printStackTrace();
	}
    }
}